// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/compiler/js-graph.h"

#include "src/code-factory.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/typer.h"
#include "src/objects-inl.h"

namespace v8 {
namespace internal {
    namespace compiler {

#define GET_CACHED_FIELD(ptr, expr) (*(ptr)) ? *(ptr) : (*(ptr) = (expr))

#define DEFINE_GETTER(name, expr) \
    Node* JSGraph::name() { return GET_CACHED_FIELD(&name##_, expr); }

        Node* JSGraph::CEntryStubConstant(int result_size, SaveFPRegsMode save_doubles,
            ArgvMode argv_mode, bool builtin_exit_frame)
        {
            if (save_doubles == kDontSaveFPRegs && argv_mode == kArgvOnStack) {
                DCHECK(result_size >= 1 && result_size <= 3);
                if (!builtin_exit_frame) {
                    Node** ptr = nullptr;
                    if (result_size == 1) {
                        ptr = &CEntryStub1Constant_;
                    } else if (result_size == 2) {
                        ptr = &CEntryStub2Constant_;
                    } else {
                        DCHECK_EQ(3, result_size);
                        ptr = &CEntryStub3Constant_;
                    }
                    return GET_CACHED_FIELD(ptr, HeapConstant(CodeFactory::CEntry(isolate(), result_size, save_doubles, argv_mode, builtin_exit_frame)));
                }
                Node** ptr = builtin_exit_frame ? &CEntryStub1WithBuiltinExitFrameConstant_
                                                : &CEntryStub1Constant_;
                return GET_CACHED_FIELD(ptr, HeapConstant(CodeFactory::CEntry(isolate(), result_size, save_doubles, argv_mode, builtin_exit_frame)));
            }
            return HeapConstant(CodeFactory::CEntry(isolate(), result_size, save_doubles,
                argv_mode, builtin_exit_frame));
        }

        Node* JSGraph::Constant(Handle<Object> value)
        {
            // Dereference the handle to determine if a number constant or other
            // canonicalized node can be used.
            if (value->IsNumber()) {
                return Constant(value->Number());
            } else if (value->IsUndefined(isolate())) {
                return UndefinedConstant();
            } else if (value->IsTrue(isolate())) {
                return TrueConstant();
            } else if (value->IsFalse(isolate())) {
                return FalseConstant();
            } else if (value->IsNull(isolate())) {
                return NullConstant();
            } else if (value->IsTheHole(isolate())) {
                return TheHoleConstant();
            } else {
                return HeapConstant(Handle<HeapObject>::cast(value));
            }
        }

        Node* JSGraph::Constant(const ObjectRef& ref)
        {
            if (ref.IsSmi())
                return Constant(ref.AsSmi());
            OddballType oddball_type = ref.AsHeapObject().GetHeapObjectType().oddball_type();
            if (ref.IsHeapNumber()) {
                return Constant(ref.AsHeapNumber().value());
            } else if (oddball_type == OddballType::kUndefined) {
                DCHECK(ref.object().equals(isolate()->factory()->undefined_value()));
                return UndefinedConstant();
            } else if (oddball_type == OddballType::kNull) {
                DCHECK(ref.object().equals(isolate()->factory()->null_value()));
                return NullConstant();
            } else if (oddball_type == OddballType::kHole) {
                DCHECK(ref.object().equals(isolate()->factory()->the_hole_value()));
                return TheHoleConstant();
            } else if (oddball_type == OddballType::kBoolean) {
                if (ref.object().equals(isolate()->factory()->true_value())) {
                    return TrueConstant();
                } else {
                    DCHECK(ref.object().equals(isolate()->factory()->false_value()));
                    return FalseConstant();
                }
            } else {
                return HeapConstant(ref.AsHeapObject().object());
            }
        }

        Node* JSGraph::Constant(double value)
        {
            if (bit_cast<int64_t>(value) == bit_cast<int64_t>(0.0))
                return ZeroConstant();
            if (bit_cast<int64_t>(value) == bit_cast<int64_t>(1.0))
                return OneConstant();
            return NumberConstant(value);
        }

        Node* JSGraph::NumberConstant(double value)
        {
            Node** loc = cache_.FindNumberConstant(value);
            if (*loc == nullptr) {
                *loc = graph()->NewNode(common()->NumberConstant(value));
            }
            return *loc;
        }

        Node* JSGraph::HeapConstant(Handle<HeapObject> value)
        {
            Node** loc = cache_.FindHeapConstant(value);
            if (*loc == nullptr) {
                *loc = graph()->NewNode(common()->HeapConstant(value));
            }
            return *loc;
        }

        void JSGraph::GetCachedNodes(NodeVector* nodes)
        {
            cache_.GetCachedNodes(nodes);
#define DO_CACHED_FIELD(name) \
    if (name##_)              \
        nodes->push_back(name##_);

            CACHED_GLOBAL_LIST(DO_CACHED_FIELD)
            CACHED_CENTRY_LIST(DO_CACHED_FIELD)
#undef DO_CACHED_FIELD
        }

        DEFINE_GETTER(AllocateInYoungGenerationStubConstant,
            HeapConstant(BUILTIN_CODE(isolate(), AllocateInYoungGeneration)))

        DEFINE_GETTER(AllocateInOldGenerationStubConstant,
            HeapConstant(BUILTIN_CODE(isolate(), AllocateInOldGeneration)))

        DEFINE_GETTER(ArrayConstructorStubConstant,
            HeapConstant(BUILTIN_CODE(isolate(), ArrayConstructorImpl)))

        DEFINE_GETTER(BigIntMapConstant, HeapConstant(factory()->bigint_map()))

        DEFINE_GETTER(BooleanMapConstant, HeapConstant(factory()->boolean_map()))

        DEFINE_GETTER(ToNumberBuiltinConstant,
            HeapConstant(BUILTIN_CODE(isolate(), ToNumber)))

        DEFINE_GETTER(EmptyFixedArrayConstant,
            HeapConstant(factory()->empty_fixed_array()))

        DEFINE_GETTER(EmptyStringConstant, HeapConstant(factory()->empty_string()))

        DEFINE_GETTER(FixedArrayMapConstant, HeapConstant(factory()->fixed_array_map()))

        DEFINE_GETTER(PropertyArrayMapConstant,
            HeapConstant(factory()->property_array_map()))

        DEFINE_GETTER(FixedDoubleArrayMapConstant,
            HeapConstant(factory()->fixed_double_array_map()))

        DEFINE_GETTER(HeapNumberMapConstant, HeapConstant(factory()->heap_number_map()))

        DEFINE_GETTER(OptimizedOutConstant, HeapConstant(factory()->optimized_out()))

        DEFINE_GETTER(StaleRegisterConstant, HeapConstant(factory()->stale_register()))

        DEFINE_GETTER(UndefinedConstant, HeapConstant(factory()->undefined_value()))

        DEFINE_GETTER(TheHoleConstant, HeapConstant(factory()->the_hole_value()))

        DEFINE_GETTER(TrueConstant, HeapConstant(factory()->true_value()))

        DEFINE_GETTER(FalseConstant, HeapConstant(factory()->false_value()))

        DEFINE_GETTER(NullConstant, HeapConstant(factory()->null_value()))

        DEFINE_GETTER(ZeroConstant, NumberConstant(0.0))

        DEFINE_GETTER(OneConstant, NumberConstant(1.0))

        DEFINE_GETTER(MinusOneConstant, NumberConstant(-1.0))

        DEFINE_GETTER(NaNConstant,
            NumberConstant(std::numeric_limits<double>::quiet_NaN()))

        DEFINE_GETTER(EmptyStateValues,
            graph()->NewNode(common()->StateValues(0,
                SparseInputMask::Dense())))

        DEFINE_GETTER(SingleDeadTypedStateValues,
            graph()->NewNode(common()->TypedStateValues(
                new (graph()->zone()->New(sizeof(ZoneVector<MachineType>)))
                    ZoneVector<MachineType>(0, graph()->zone()),
                SparseInputMask(SparseInputMask::kEndMarker << 1))))

#undef DEFINE_GETTER
#undef GET_CACHED_FIELD

    } // namespace compiler
} // namespace internal
} // namespace v8
